#ifndef CARDTABLE_H
#define CARDTABLE_H

#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QSoundEffect>
#include "card.h"
#include "animation.h"
#include "json/json.hpp"

using json = nlohmann::json;

#define BONUS_COUNT 4
#define SUITED_COUNT 9
#define FLOWER_COUNT 1

#define PLAYING_SLOTS_COUNT 8
#define BONUS_SLOTS_COUNT 3
#define SUITED_SLOTS_COUNT 3

class SuitedCardCounter
{
public:
    SuitedCardCounter()
    {
        suited_types[0] = Coins;
        suited_types[1] = Bamboo;
        suited_types[2] = Characters;
        initialize();
    }

private:
    int current_number;
    int types_index;
    QMap<CardType, bool> placed_maps[SUITED_COUNT];
    CardType suited_types[3];
    int slot_index;
    QMap<CardType, int> slot_indexs;

public:
    void reset() { initialize(); }

    bool isNext(CardType type, int number)
    {
        if (number == 1)
        {
            slot_indexs[type] = slot_index++;
            suited_types[slot_indexs[type]] = type;
            placed_maps[0][type] = true;
            return true;
        }
        else if (number == 2)
        {
            if (placed_maps[0][type])
            {
                placed_maps[1][type] = true;
                return true;
            }
        }
        else
        {
            for (int i = 0; i < 2; ++i)
            {
                for (auto iter = placed_maps[i].begin(); iter != placed_maps[i].end(); ++iter)
                {
                    if (!iter.value()) return false;
                }
            }
            while (placed_maps[current_number - 1][suited_types[types_index]])
            {
                next();
            }
            if (number == current_number && suited_types[types_index] == type)
            {
                placed_maps[number - 1][type] = true;
                next();
                return true;
            }
        }
        return false;
    }

    void manulPlace(CardType type, int number)
    {
        placed_maps[number - 1][type] = true;
    }

    int slotIndex(CardType type)
    {
        return slot_indexs[type];
    }

private:
    void initialize()
    {
        for (int i = 0; i < 3; ++i)
        {
            for (int j = 0; j < SUITED_COUNT; ++j)
            {
                placed_maps[j][suited_types[i]] = false;
            }
            slot_indexs[suited_types[i]] = i;
        }
        slot_index = 0;
        current_number = 3;
        types_index = 0;
    }

    void next()
    {
        types_index++;
        if (types_index == 3)
        {
            current_number++;
            types_index = 0;
        }
    }
};

class BonusCollectButton: public QObject, public QGraphicsPixmapItem
{
    Q_OBJECT
public:
    BonusCollectButton(CardType type, QGraphicsItem *parent = nullptr):
        QObject(nullptr), QGraphicsPixmapItem(parent), type(type)
    {
        is_pressed = false;
        setStatus(Up);
        setTransformationMode(Qt::SmoothTransformation);
    }

signals:
    void clicked(CardType type);

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *e) override
    {
        if (e->button() != Qt::LeftButton) return;
        if (status != Active) return;
        is_pressed = true;
    }

    void mouseReleaseEvent(QGraphicsSceneMouseEvent *e) override
    {
        if (e->button() != Qt::LeftButton) return;
        if (is_pressed)
        {
            is_pressed = false;
            emit clicked(type);
        }
    }

public:
    enum Status {Up, Active, Down};

private:
    CardType type;
    Status status;
    bool is_pressed;

public:
    void setStatus(Status s)
    {
        status = s;
        switch (status) {
        case Up:
            setPixmap(QPixmap(QString(":/textures/button_%1_up.png").arg(typestr())));
            break;
        case Active:
            setPixmap(QPixmap(QString(":/textures/button_%1_active.png").arg(typestr())));
            break;
        case Down:
            setPixmap(QPixmap(QString(":/textures/button_%1_down.png").arg(typestr())));
            break;
        default:
            break;
        }
    }

private:
    QString typestr()
    {
        switch (type) {
        case White: return "white";
        case Red: return "red";
        case Green: return "green";
        default: return "";
        }
    }
};

enum SlotType
{
    PlayingSlot,
    BonusSlot,
    SuitedSlot,
    FlowerSlot
};

class CardTable : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    CardTable();
    virtual ~CardTable();

private:
    struct CardSlot
    {
        SlotType type; // 卡槽类型
        int index; // 卡槽索引
    };
    std::vector<Card*> source_cards;
    std::vector<Card*> playing_slots[PLAYING_SLOTS_COUNT];
    std::vector<Card*> suited_slots[SUITED_SLOTS_COUNT];
    std::vector<Card*> bonus_slots[BONUS_SLOTS_COUNT];
    Card *flower_card;
    QGraphicsPixmapItem *background;
    bool is_playing_game;
    SuitedCardCounter suited_card_counter; // 数字牌自动收集计数器
    int timer_id; // 动画计时器
    bool is_playing_animations; // 是否正在播放动画
    QList<AnimationGroup*> active_animation_groups;
    QMap<CardType, BonusCollectButton*> bonus_collect_buttons;
    QMap<CardType, QVector<CardSlot>> bonus_cards_to_collect;
    QSoundEffect *card_pick_up; // 拖拽卡牌音效
    QSoundEffect *card_place; // 放置卡牌音效
    // 拖放操作变量
    bool is_pressed;
    QPointF begin_point;
    std::vector<Card*> pressed_cards;
    std::vector<std::pair<QPointF, int>> pressed_cards_saved_states;
    SlotType moving_slot_type;
    int moving_slot_index;

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void timerEvent(QTimerEvent *event) override;

signals:
    void gameFinished();

public slots:
    void slotBonusButtonClicked(CardType type);
    void slotGameFinished();

public:
    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
    void startNewGame();
    json getPlayingData();
    void initializeByPlayingData(const json &data);

private:
    void savePressedCards();
    void restorePressedCards();
    QVector<CardSlot> placeableSlots();
    bool moveable(std::vector<Card*> &cards);
    bool isFinished();
    void shuffle(std::vector<Card*> &cards);
    void autoCollect();
    bool collectFlowerCard(SequentialAnimationGroup *group);
    bool collectSuitedCards(SequentialAnimationGroup *group);
    bool collectBonusCards();
    void collectCard(const CardSlot &src, const CardSlot &dst, int startZValue, AnimationGroup *group);
    void moveCards(std::vector<Card*>& source, std::vector<Card*>& target, size_t num);
    double intersectArea(const QRectF &a, const QRectF &b) const;
    QPointF cardSlotPosition(const CardSlot &slot);
    QRectF cardSlotRect(const CardSlot &slot);
    int suitedSlotIndex(CardType type);
    QPointF suitedSlotPosition(CardType type);
    QPointF playingCardPosition(int slotIndex, int cardIndex);
    void startAnimationGroup(AnimationGroup *group);
    void releaseAnimationGroup(AnimationGroup *group);
    Animation *moveAnimation(Card *card, const QPointF &target, bool flip = false,
                             tweeny::easing::enumerated easing = tweeny::easing::enumerated::linear,
                             int duration = 200, bool playSound = true);
};

#endif // CARDTABLE_H
